﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace NhWeather.Modbus
{
    public abstract class BaseFrame
    {
        /// <summary>
        /// 设备地址
        /// </summary>
        public byte Address { get; set; }

        /// <summary>
        /// 控制码
        /// </summary>
        public byte Code { get; set; }

        /// <summary>
        /// 控制码、效验码之间的数据
        /// </summary>
        public byte[] Data { get; set; }

        /// <summary>
        /// 帧对应的地址控制码，方便跟踪、区分
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return $"{Address:X}H、{Code:X}H";
        }

        /// <summary>
        /// 效验数组前len-2个，CRC加到最后2个
        /// </summary>
        /// <param name="frame">要校验的帧</param>
        public static void AddCrc(ref byte[] frame)
        {
            lock ("AddCrc")//静态方法，防止重入
            {
                byte crcLo, crcHi;
                (crcLo, crcHi) = CRC16(frame, frame.Length - 2);
                frame[^2] = crcLo;
                frame[^1] = crcHi;
            }
        }

        /// <summary>
        /// CRC效验
        /// </summary>
        /// <param name="byts">待效验数组</param>
        /// <param name="len">待效验长度</param>
        /// <returns></returns>
        public static (byte CrcLo, byte CrcHi) CRC16(byte[] byts, int len)
        {
            lock ("CRC16")
            {
                byte u8CRC16Lo = 0xFF, u8CRC16Hi = 0xFF;
                int i, Flag;
                ushort u16C = 0xA001;//多项式码0xA001

                for (i = 0; i < len; i++)
                {
                    u8CRC16Lo ^= byts[i];//每一个数据与CRC寄存器进行异或 
                    UInt16 u16CRC16 = (UInt16)((u8CRC16Hi << 8) + u8CRC16Lo);//+权限高于<<
                    for (Flag = 0; Flag <= 7; Flag++)
                    {
                        u16CRC16 >>= 1;//右移一位    
                        if ((u8CRC16Lo & 0x01) == 0x01)//如果LSB为1，则与多项式码进行异或   
                        {
                            u16CRC16 ^= u16C;
                        }
                        u8CRC16Lo = (byte)u16CRC16;//低8位保存，为下循环后比较最低位
                    }
                    u8CRC16Hi = (byte)(u16CRC16 >> 8);
                }
                return (u8CRC16Lo, u8CRC16Hi);
            }
        }
    }

    /// <summary>
    /// 检查效验码，通过则提取Address,Code,Data
    /// </summary>
    public class FrameValid : BaseFrame
    {

        /// <summary>
        /// 效验码通过
        /// </summary>
        public bool IsValid { get; set; }

        public FrameValid(byte[] aByts)
        {
            if (aByts == null)
            {
                Debug.WriteLine("Modbus() byts[]为null");
            }
            else
            {
                int iCrcSite;
                if (aByts.Length < 8) return;

                if (aByts[1] == 0x10)
                {
                    iCrcSite = 7 + aByts[6];
                }
                else
                {
                    iCrcSite = 6;
                }

                if (aByts.Length < iCrcSite + 2) return;

                byte bytCrcLo, bytCrcHi;
                (bytCrcLo, bytCrcHi) = CRC16(aByts, iCrcSite);//计算CRC时不包含原CRC
                IsValid = (bytCrcLo == aByts[iCrcSite]) && (bytCrcHi == aByts[iCrcSite + 1]);
                if (IsValid)
                {
                    Address = aByts[0];
                    Code = aByts[1];
                    Data = new byte[iCrcSite - 2];
                    Array.Copy(aByts, 2, Data, 0, Data.Length);//包含数据长度字节
                }
            }
        }
    }
}
